﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using System;

public class FieldOfViewDemo : MonoBehaviour
{
    private int mWidth = 0;
    private int mHeight = 0;
    private int mCellsize = 0;
    private List<LightGridData> mGrid = null;
    private int mClearColor = 0x241509;

    private int mMode = 0;
    private bool mMoveLight = true;
    private int mAmbientLight = 0x403020;
    private Vector2 mLightPos = new Vector2();          //鼠标灯光的位置
    private Vector2 mTmp = new Vector2();
    private bool mWideRays = false;

    public Text mModeLabel;


    // Update is called once per frame
    void Update()
    {
        ClearGrid();
        int range = 10;
        int color = 0xD0D0B0;
        int baseColor = 0x000000;
        string status = string.Empty;
        float dt = Time.time;

        switch (mMode)
        {
            case 0:
                status = "Partial Occlusion Recursive";
                AddShadowLightRecursive(mLightPos, range, color);
                break;
        }

        //finally render lightgrid to buffer
        RenderBuffer();
        //print mode and a deltaTime estimate
        mModeLabel.text = " " + (Time.time - dt) + "ms";
    }

    private void ClearGrid()
    {
        for (int i = 0; i < mWidth * mHeight; i++)
            mGrid[i].Clear(mAmbientLight);
    }

    private void RenderBuffer()
    {

        Sprite s = null;

        /*
    //clear previous content
    mBuffer.fillRect(_buffer.rect, _clearColor);
			
    //render light-grid
    var rect:Rectangle = new Rectangle(0,0,_cellsize-2,_cellsize-2);
    for(var y:int = 0; y < _height; y++)
        for(var x:int = 0; x < _width; x++)
        {
            rect.x = x*_cellsize+1;
            rect.y = y*_cellsize+1;
            _buffer.fillRect(rect, fetch(x,y).color);
        }
			
    //render lightsource as a little x
    var px:int = (_lightPos.x * _cellsize);
    var py:int = (_lightPos.y * _cellsize);
    for(x = -3; x <= 3; x++)
        _buffer.setPixel(px+x, py, 0x000000);
    for(y = -3; y <= 3; y++)
        _buffer.setPixel(px, py+y, 0x000000);
         * 
         * */
    }

    private LightGridData Fetch(int x, int y)
    {
        if (x >= 0 && x < mWidth && y >= 0 && y < mHeight)
            return mGrid[y * mWidth + x];                       //第几行第几列
        else
            return null;
    }



    private void AddShadowLightRecursive(Vector2 pos, double range, int color)
    {
        //this is the recursive variant of the algorithm
        int minx = (int)Math.Max(0, pos.x - range);
        int maxx = (int)Math.Min(mWidth - 1, pos.x + range);
        int miny = (int)Math.Max(0, pos.y - range);
        int maxy = (int)Math.Min(mHeight - 1, pos.y + range);

        int centerY = (int)Math.Max(0, Math.Ceiling(pos.y) - 1);
        int centerX = (int)Math.Max(0, Math.Ceiling(pos.x) - 1);

        //reset occlusion(还原全部的格子)
        for (int y = miny; y <= maxy; y++)
            for (int x = minx; x <= maxx; x++)
                mGrid[y * mWidth + x].ResetOcclusion();

        //calculate occlusion by starting from the edges, eventually visiting all cells in the relevant rect
        Fetch(minx, miny).CalclOcclusionRecursive(pos, true);               //左下
        Fetch(minx, maxy).CalclOcclusionRecursive(pos, true);               //左上

        //clear center (necessary HACK becuse left and right marching eval uses different Angle-normalization)
        for (int y = miny; y <= centerY; y++)
            mGrid[y * mWidth + centerX].ResetOcclusion();

        Fetch(maxx, miny).CalclOcclusionRecursive(pos, false);              //右下
        Fetch(maxx, maxy).CalclOcclusionRecursive(pos, false);              //右上 

        //shade based on occlusion & distance from light source
        double rSqr = range * range;
        for (int y = miny; y <= maxy; y++)
            for (int x = minx; x <= maxx; x++)
            {
                //cell receives light based on intensity and occlusion
                //intensity is calculated based on distance from lightsource
                double dSqr = pos.DistanceSquared(mTmp.Reset(x + 0.5f, y + 0.5f));
                double intensity = Math.Max(0.0, 1.0 - dSqr / rSqr);
                LightGridData ld = mGrid[y * mWidth + x];
                ld.AddScaled(color, intensity * (1 - ld.mOcclusion));
            }
    }





}

public static class Vector2Tools
{
    public static Vector2 Reset(this Vector2 cur, float vx, float vy)
    {
        cur.x = vx;
        cur.y = vy;
        return cur;
    }

    public static double DistanceSquared(this Vector2 cur, Vector2 b)
    {
        double dx = cur.x - b.x;
        double dy = cur.y - b.y;
        return (dx * dx + dy * dy);
    }
}

